package sindconsole.misc.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sindconsole.misc.bean.Dict;
import sindconsole.misc.bean.DictItem;
import sindconsole.misc.dao.DictItemMapper;
import sindconsole.misc.dao.DictMapper;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class DictServiceImpl implements DictService {

    @Autowired
    DictMapper dictMapper;

    @Autowired
    DictItemMapper dictItemMapper;

    @Override
    public List<Dict> findAllDicts() {
        return dictMapper.selectList(new QueryWrapper<Dict>());
    }

    @Override
    public int createDict(Dict dict) {
        return dictMapper.insert(dict);
    }

    @Override
    public int updateDict(Dict dict) {
        return dictMapper.updateById(dict);
    }

    @Override
    @Transactional
    public int deleteDict(long id) {
        int r = dictMapper.deleteById(id);
        r += dictItemMapper.delete(new QueryWrapper<DictItem>().lambda().eq(DictItem::getDictId, id));
        return r;
    }

    @Override
    public List<DictItem> findDictItems(long dictId) {
        return dictItemMapper.selectList(new QueryWrapper<DictItem>().lambda().eq(DictItem::getDictId, dictId));
    }

    @Override
    @Cacheable(cacheNames = "sindconsole.misc.dict", key = "#dictId + ':tree'")
    public List<DictItem> treeDictItems(long dictId) {
        List<DictItem> items = findDictItems(dictId);
        Map<Long, DictItem> itemMap = items.stream().collect(Collectors.toMap(DictItem::getId, Function.identity()));
        for(Iterator<DictItem> it = items.iterator(); it.hasNext(); ) {
            DictItem item = it.next();
            if(item.getParentId() != null) {
                DictItem parent = itemMap.get(item.getParentId());
                if(parent != null) {
                    parent.addChild(item);
                    it.remove();
                }
            }
        }
        return items;
    }

    @Override
    @CacheEvict(cacheNames = "sindconsole.misc.dict", key = "#dictItem.dictId + ':tree'")
    public int createDictItem(DictItem dictItem) {
        return dictItemMapper.insert(dictItem);
    }

    @Override
    @CacheEvict(cacheNames = "sindconsole.misc.dict", key = "#dictItem.dictId + ':tree'")
    public int updateDictItem(DictItem dictItem) {
        return dictItemMapper.updateById(dictItem);
    }

    @Override
    @CacheEvict(cacheNames = "sindconsole.misc.dict", key = "#dictId + ':tree'")
    @Transactional
    public int deleteDictItem(long dictId, long id) {
        int r = dictItemMapper.deleteById(id);
        r += dictItemMapper.updateAllParentId(null, id);
        return r;
    }

    @Override
    @CacheEvict(cacheNames = "sindconsole.misc.dict", key = "#dictId + ':tree'")
    public int relinkDictItems(long dictId, Long newParentId, long id) {
        return dictItemMapper.updateParentId(newParentId, id);
    }

    @Override
    public List<String> searchPathByDictItemId(long dictItemId) {
        DictItem item = dictItemMapper.selectById(dictItemId);
        if(item == null) return null;
        LambdaQueryWrapper<DictItem> criteria = new QueryWrapper<DictItem>().lambda();
        criteria.eq(DictItem::getDictId, item.getDictId());
        List<DictItem> items = dictItemMapper.selectList(criteria);
        Map<Long, DictItem> itemMap = items.stream().collect(Collectors.toMap(i -> i.getId(), i -> i));
        List<String> result = new LinkedList<>();
        while(item != null) {
            result.add(0, item.getLabel());
            item = itemMap.get(item.getParentId());
        }
        return result;
    }

    @Override
    public DictItem searchDictItemByPath(List<String> path) {
        List<DictItem> items = dictItemMapper.selectItemsByLabels(path);
        return searchDictItemByPath(path, 0, items, null);
    }

    private DictItem searchDictItemByPath(List<String> path, int i, List<DictItem> items, DictItem current) {
        if(i >= path.size()) return current;
        String label = path.get(i);
        for(DictItem item : items) {
            if(label.equals(item.getLabel()) && (current == null || (current != null && current.getDictId() == item.getDictId())))
                return searchDictItemByPath(path, i + 1, items, item);
        }
        return null;
    }

}
